import torch
import numpy as np
import sys
sys.path.append("..") # 为了导入上层目录的d2lzh_pytorch
from basic_knowledge.d2lzh_pytorch import data_iter
from basic_knowledge.d2lzh_pytorch import squared_loss
from basic_knowledge.d2lzh_pytorch import linreg
from basic_knowledge.d2lzh_pytorch import sgd


# 生成1000个样本的训练集，模型的真实权重w1=2,w2=-3.4,b=4.2
num_inputs = 2
num_examples = 1000
true_w = [2, -3.4]
true_b = 4.2
# 产生一个标准正态分布的张量
features = torch.randn(num_examples, num_inputs,
                       dtype=torch.float32)
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()),
                       dtype=torch.float32)

# 初始化模型参数:权重为均值0标准差0.01的正态随机数，偏差为0
w = torch.tensor(np.random.normal(0, 0.01, (num_inputs, 1)),
                 dtype=torch.float32)
b = torch.zeros(1, dtype=torch.float32)
# 自动求梯度
w.requires_grad_(requires_grad=True)
b.requires_grad_(requires_grad=True)

# 训练模型，模型的相关定义在在d2lzh_pytorch中实现
lr = 0.03  # lr为学习率
num_epochs = 3  # 迭代周期
batch_size = 10

for epoch in range(num_epochs):  # 训练模型一共需要num_epochs个迭代周期
    # 在每一个迭代周期中，会使用训练数据集中所有样本一次（假设样本数能够被批量大小整除）。X
    # 和y分别是小批量样本的特征和标签
    for X, y in data_iter(batch_size, features, labels):
        l = squared_loss(linreg(X, w, b), y).sum()  # l是有关小批量X和y的损失
        l.backward()  # 小批量的损失对模型参数求梯度
        sgd([w, b], lr, batch_size)  # 使用小批量随机梯度下降迭代模型参数

        # 不要忘了梯度清零
        w.grad.data.zero_()
        b.grad.data.zero_()
    train_l = squared_loss(linreg(features, w, b), labels)
    print('epoch %d, loss %f' % (epoch + 1, train_l.mean().item()))

print(true_w, '\n', w)
print(true_b, '\n', b)